#ifndef UTILITY_HPP
#define UTILITY_HPP

#include <boost/chrono.hpp>
#include <boost/filesystem.hpp>
#include <boost/random/random_device.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/test/included/unit_test.hpp>
#include <boost/timer/timer.hpp>

const std::string kResourcesDir =
#if defined(CURRENT_DIR_NAME)
    CURRENT_DIR_NAME;
#else
    "/home/yangjian/Workspace/project/gpu_develop/opencl";
#endif

void GeneratorRandom(int* data, std::size_t length)
{
    boost::random::random_device              rng;
    boost::random::uniform_int_distribution<> dist(0, 1024);
    for (size_t i = 0; i < length; i++)
    {
        data[i] = dist(rng);
    }
    return;
}

std::string LoadProgramFile(const std::string& filename)
{
    const std::string kDir     = kResourcesDir + "/cl/";
    auto              filepath = kDir + filename;
    if (!boost::filesystem::exists(filepath))
    {
        BOOST_FAIL(boost::format("File %s does not exist") % filepath);
        return "";
    }

    std::ifstream file(filepath.c_str());
    std::string   source(std::istreambuf_iterator<char>(file),
                       (std::istreambuf_iterator<char>()));
    return source;
}

template <typename TimeUnit = boost::chrono::milliseconds>
class ChronoTimer {
public:
    explicit ChronoTimer(const std::string& name = "")
        : name_(name), start_(boost::chrono::high_resolution_clock::now())
    {
    }

    typename TimeUnit::rep elapsed()
    {
        auto end = boost::chrono::high_resolution_clock::now();
        return boost::chrono::duration_cast<TimeUnit>(end - start_).count();
    }

    void print_elapsed(const std::string& unit = "")
    {
        auto duration = elapsed();
        if (name_.empty())
        {
            std::cout << "Elapsed time: " << duration << " " << unit
                      << std::endl;
        }
        else
        {
            std::cout << name_ << " took: " << duration << " " << unit
                      << std::endl;
        }
    }

private:
    std::string                                                     name_;
    boost::chrono::time_point<boost::chrono::high_resolution_clock> start_;
};

// RAII风格的自动计时器
template <typename TimeUnit = boost::chrono::microseconds>
class ScopedTimer {
public:
    explicit ScopedTimer(const std::string& name,
                         int                repeat = 1,
                         const std::string& unit   = "us")
        : name_(name), repeat_(repeat), unit_(unit), timer_()
    {
    }

    ~ScopedTimer()
    {
        auto duration = timer_.elapsed() / repeat_;
        std::cout << name_ << " took: " << duration << " " << unit_
                  << std::endl;
    }

private:
    std::string           name_;
    int                   repeat_;
    std::string           unit_;
    ChronoTimer<TimeUnit> timer_;
};

// 方便使用的宏
#define SCOPED_TIMER(name) ScopedTimer<> _scoped_timer_(name)
#define SCOPED_TIMER_REPEAT(name, repeat) \
    ScopedTimer<> _scoped_timer_(name, repeat)

// 获取平台和设备信息的辅助函数
inline std::vector<cl_platform_id> GetPlatforms()
{
    cl_uint num_platforms = 0;
    cl_int  err           = clGetPlatformIDs(0, nullptr, &num_platforms);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_REQUIRE_GT(num_platforms, 0);

    std::vector<cl_platform_id> platforms(num_platforms);
    err = clGetPlatformIDs(num_platforms, platforms.data(), nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    return platforms;
}

inline std::vector<cl_device_id> GetDevices(
    cl_platform_id platform, cl_device_type type = CL_DEVICE_TYPE_ALL)
{
    cl_uint num_devices = 0;
    cl_int  err = clGetDeviceIDs(platform, type, 0, nullptr, &num_devices);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);
    BOOST_REQUIRE_GT(num_devices, 0);

    std::vector<cl_device_id> devices(num_devices);
    err = clGetDeviceIDs(platform, type, num_devices, devices.data(), nullptr);
    BOOST_CHECK_EQUAL(err, CL_SUCCESS);

    return devices;
}

#endif